#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
@FileName : newton.py
@Version : 1.0
@Date : 2023/6/19 11:13
"""
import numpy as np
import math
from root_paras import Paras
from typing import Tuple, Callable
from find_section import find_section

Tri = Tuple[float, float, float]
Dou = Tuple[float, float]


def newton(fun11: Callable[[float], Dou], paras: Paras = Paras()) -> Tri:  # 迭代k次,包括x0在内共k+1个数
    def fun(x):
        return fun11(x)[0]

    a, b = find_section(fun, paras)
    if fun(a) * fun(b) > 0:
        raise ValueError("fun(a_)*fun(b) <= 0 not satisfied!")
    if b < a:
        b, a = a, b

    n: int = 0
    sol_tol = paras.sol_tol
    sec_tol = paras.sec_tol
    loops = paras.loops
    xi: float = b
    while math.fabs(b - a) > sec_tol:
        n += 1
        if n > loops:
            raise RuntimeError(f'Newton iteration: loop over {loops} times, with section {b - a}')

        y, dy1 = fun11(xi)
        if math.fabs(dy1) < sec_tol:
            print(f'Newton method, dy1 = {dy1} ~> 0')

        xi += - y / dy1
        if math.fabs(fun(b) - 0) <= sol_tol:
            break

        if fun(a) * fun(xi) < 0:
            b = xi
        else:
            a = xi

    if math.fabs((val := fun(xi)) - 0) <= sol_tol:
        print(n)
        return xi, a, b
    else:
        raise RuntimeError(f'Secant iteration: did not converge to zero, instead -> {val}')


if __name__ == '__main__':
    fun = lambda x: (x ** 3 + x - 1, 3 * x ** 2 + 1)
    res = newton(fun)
    print(res)
